home *** CD-ROM | disk | FTP | other *** search
- // ___ ___
- // _/ /_______\ \_ ___ ___ __ _ _ __ ___ ___
- //__// / _______ \ \\___/ \___
- //_/ | ' \__ __/ ` | \_/ © Copyright 1999-2000, Christopher Page \__
- // \ | | | |__ | | / \ Released as Free Software under the GNU GPL /
- // >| . | _/ . |< >--- --- -- - - -- --- ---<
- // / \ \ | | / / \ / This file is part of the VersionCopy source \
- // \ \ \_/ \_/ / / \ and it is released under the GNU GPL. Please /
- // \ \ / / \ read the "COPYING" file which should have /
- // //\ \_________/ /\\ //\ been included in the distribution arc. /
- //- --\ _______ /-- - --\ for full details of the license /-----
- //-----\_/ \_/---------\ ___________________________________ /------
- // \_/ \_/
- //
- // Description:
- //
- // VersionCopy version aware copy command
- //
- // Functions:
- //
- // BOOL IsDirectory (STRPTR TestName)
- // STRPTR FindSubBuffer (UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen)
- // VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData)
- // void FreeVersion (struct VersionData *OldData)
- // LONG FileCopy (STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
- // void CopyAttributes (struct FileInfoBlock *SourceInfo, STRPTR TargetName)
- // LONG VersionCopy (struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
- //
- // Detail:
- //
- // VersionCopy is a cut-down "Copy" command which is aware of the version information
- // within the file being copied. Before copying the source file it checks whether the
- // destination already exists and, if it does, the version numbers of the source and
- // destination are compared. The source is only copied over the destination if the
- // source version or revision is greater than that of the destination. Unlike Copy,
- // wildcards and directory copy are not supported - indeed these facilities would be
- // somewhat meaningless for the applications this command is intended.
- //
- // Fold Markers:
- //
- // Start: /*GFS*/
- // End: /*GFE*/
-
- #include<exec/exec.h>
- #include<dos/dos.h>
-
- #include<clib/exec_protos.h>
- #include<clib/dos_protos.h>
-
- #include<ctype.h>
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
-
- #include"VersionCopy_rev.h"
-
- const char Copyright[] = "© copyright 2000 Chris Page";
- const char Version[] = VERSTAG;
-
- // Typing saver! :)
- #define ShowMessage(TextData) if(!((BOOL)ArgResult[OPT_QUIET])) printf("%s\n", TextData)
-
- // Stuff for ReadArgs()
- #define ARGS_TEMPLATE "FROM/A,TO/A,QUIET/S,BUF=BUFFER/K/N,CLONE/S,DATE/S,COM/S,NOPRO/S,IGNORENAME/S"
- #define OPT_FROM 0
- #define OPT_TO 1
- #define OPT_QUIET 2
- #define OPT_BUFFER 3
- #define OPT_CLONE 4
- #define OPT_DATE 5
- #define OPT_COM 6
- #define OPT_NOPRO 7
- #define OPT_IGNORE 8
- #define OPT_COUNT 9
-
- // Note this does not consider dates!
- struct VersionData
- {
- STRPTR Name ; // Name given after $VER:, NULL if not found
- LONG Version ; // Version number
- LONG Revision; // Revision, 0 if no revision given.
- };
-
- // Prototype type things..
- BOOL IsDirectory (STRPTR TestName);
- STRPTR FindSubBuffer (UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen);
- struct VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData);
- void FreeVersion (struct VersionData *OldData);
- LONG FileCopy (STRPTR SourceName, STRPTR TargetName, ULONG BufferSize);
- void CopyAttributes (struct FileInfoBlock *SourceInfo, STRPTR TargetName);
- LONG VersionCopy (struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize);
-
-
- // Globals...
- char WorkBuffer[80] ; // Yeah, this is nasty but it saves some messing about later
- ULONG ArgResult [OPT_COUNT]; // ReadArgs store.
-
-
- /* BOOL IsDirectory(STRPTR) */
- /* -=-=-=-=-=-=-=-=-=-=-=-= */
- /* If the specified filename is actually a directory this returns TRUE. */
- /* */
- /* Parameters: */
- /* TestName Name of the object to identify. */
-
- /*GFS*/ BOOL IsDirectory(STRPTR TestName)
- {
- struct FileInfoBlock *TestFIB ;
- BPTR TestLock;
- BOOL IsDir = TRUE;
-
- if(TestFIB = AllocDosObject(DOS_FIB, NULL)) {
- if(TestLock = Lock(TestName, SHARED_LOCK)) {
- Examine(TestLock, TestFIB);
-
- IsDir = (TestFIB -> fib_DirEntryType >= 0);
-
- UnLock(TestLock);
- } else {
- IsDir = FALSE;
- }
- FreeDosObject(DOS_FIB, TestFIB);
- }
-
- return(IsDir);
- }/*GFE*/
-
-
- /* STRPTR FindSubBuffer(UBYTE *, UBYTE *, LONG, LONG) */
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
- /* This is basically a version of strstr which will work for non-string data */
- /* (ie: buffers containing arbitrary data, not just NULL-terminated strings) */
- /* */
- /* Parameters: */
- /* Source Buffer to scan */
- /* Pattern Pattern to find in Source */
- /* SourceLen Length of the source buffer in bytes */
- /* PatternLen Length of the pattern in bytes */
-
- /*GFS*/ STRPTR FindSubBuffer(UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen)
- {
- LONG SourcePos = 0;
-
- while(SourcePos <= SourceLen) {
- if(!memcmp(&Source[SourcePos], Pattern, PatternLen)) {
- return(&Source[SourcePos]);
- } else {
- SourcePos ++;
- }
- }
-
- return(NULL);
- }/*GFE*/
-
-
- /* VersionData *FindVersion(BPTR, FileInfoBlock *) */
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
- /* This routine loads the file specified by the provided file handle into a */
- /* memory buffer and searches it for the $VER: string that identifies the */
- /* start of an Amiga version string. If it finds this the name, version and */
- /* revision are copied into a VersionData structure and returned. Use FreeVec*/
- /* to release the name buffer and VersionData structure returned by this */
- /* If this returns NULL then IoErr() will contain a code indicating why */
- /* */
- /* Parameters: */
- /* Source File handle for the file to load */
- /* SourceData FIB filled in by calling ExamineFH() on the source file */
-
- /*GFS*/ struct VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData)
- {
- struct VersionData *NewData;
- STRPTR Buffer ;
- STRPTR Search ;
- STRPTR Start ;
- BOOL Spaced = FALSE;
- BOOL Complete = FALSE;
- LONG ErrorCode = 0;
-
- // This is a hack to get around vbcc not putting the Version string near
- // the start of the file. If FindBuffer[] = "$VER:" then Version VersionCopy
- // gets confused between the $VER: for FindBuffer and the one for the version
-
- char FindBuffer[] = " VER:";
- FindBuffer[0] = '$';
-
- if(Buffer = AllocVec(SourceData -> fib_Size, MEMF_ANY)) {
- Read(Source, Buffer, SourceData -> fib_Size);
-
- Search = FindSubBuffer(Buffer, FindBuffer, SourceData -> fib_Size - 6, 6);
-
- if(Search) {
- // Skip the version header
- Search += 6;
-
- // Skip any leading whitespace
- while(*Search && (*Search == ' ')) Search++;
-
- // Remember the start location
- Start = Search;
-
- // Look for the first digit after a space
- do {
- if(isdigit(*Search) && Spaced) {
- Complete = TRUE;
- } else {
- Spaced = (*Search == ' ');
- Search++;
- }
- } while(!Complete && *Search);
-
- if(NewData = AllocVec(sizeof(struct VersionData), MEMF_ANY|MEMF_CLEAR)) {
-
- // Allocate space for the name and copy it.
- if(NewData -> Name = AllocVec(Search - Start, MEMF_ANY|MEMF_CLEAR)) {
- CopyMem(Start, NewData -> Name, Search - Start - 1);
-
- // got a version number?
- if(Complete) {
- NewData -> Version = atol(Search);
-
- // Try to locate the revision
- while(*Search && (*Search != '.')) Search++;
-
- if(*Search == '.') {
- Search++;
- Start = Search;
-
- // whack a NULL in at the cirst non-digit encountered
- while(isdigit(*Search)) Search++;
- *Search = '\0';
-
- NewData -> Revision = atol(Start);
- }
-
- FreeVec(Buffer);
- SetIoErr(0);
- return(NewData);
- }
- } else {
- ErrorCode = ERROR_NO_FREE_STORE;
- ShowMessage("Unable to allocate version name buffer");
- }
- FreeVec(NewData);
- } else {
- ErrorCode = ERROR_NO_FREE_STORE;
- ShowMessage("Unable to allocate version data");
- }
- } else {
- ErrorCode = ERROR_OBJECT_WRONG_TYPE;
- if(!((BOOL)ArgResult[OPT_QUIET])) printf("%s does not contain version information\n", SourceData -> fib_FileName);
- }
-
- FreeVec(Buffer);
- } else {
- ErrorCode = ERROR_NO_FREE_STORE;
- ShowMessage("Unable to allocate search buffer");
- }
-
- SetIoErr(ErrorCode);
- return(NULL);
- }/*GFE*/
-
-
- /* void FreeVersion(VersionData *) */
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
- /* Small routine to free the specified VersionData structure - if the pointer*/
- /* is not NULL then the Name field is freed (if required) and the the struct */
- /* itself is released. */
- /* */
- /* Parameters: */
- /* OldData The VersionData structure to free. */
-
- /*GFS*/ void FreeVersion(struct VersionData *OldData)
- {
- if(OldData) {
- if(OldData -> Name) FreeVec(OldData -> Name);
- FreeVec(OldData);
- }
- }/*GFE*/
-
-
- /* LONG FileCopy(STRPTR, STRPTR, ULONG) */
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
- /* This does the actual duplication of the source file, copying BufferSize */
- /* chunks of the file until the whole file is copied or the user does a */
- /* CTRL-C to abort it the copy. Errors in reading and writing are detected. */
- /* */
- /* Parameters: */
- /* SourceName Name of the file to copy */
- /* TargetName Name of the destination file */
- /* BufferSize Size of the copy buffer in bytes. */
-
- /*GFS*/ LONG FileCopy(STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
- {
- BPTR SourceFile, DestFile ;
- UBYTE *CopyBuffer ;
- LONG CopyLength ;
- LONG WriteLength ;
- LONG ReturnCode = RETURN_WARN;
-
- if(CopyBuffer = (UBYTE *)AllocVec(BufferSize, MEMF_ANY)) {
- if(SourceFile = Open(SourceName, MODE_OLDFILE)) {
- if(DestFile = Open(TargetName, MODE_NEWFILE)) {
-
- // Read & write BufferSize chunks of the file, allowing user abort and
- // filesystem full handling..
- do {
- WriteLength = 0;
- if(CopyLength = Read(SourceFile, CopyBuffer, BufferSize)) {
-
- // Allow user abort..
- if(!(SetSignal(0, 0) & SIGBREAKF_CTRL_C)) {
- WriteLength = Write(DestFile , CopyBuffer, CopyLength);
- }
- }
- } while((CopyLength > 0) && (WriteLength == CopyLength));
-
- // should catch the last error
- if((CopyLength < 0) || (WriteLength != CopyLength)) ReturnCode = IoErr();
- Close(DestFile);
-
- // Failed to write the last read, erase destination..
- if((WriteLength != CopyLength) && (CopyLength > 0)) {
- DeleteFile(TargetName);
-
- if(ReturnCode && !(BOOL)ArgResult[OPT_QUIET]) {
- Fault(ReturnCode, NULL, WorkBuffer, 80);
- printf("Unable to write %s: %s\n", TargetName, WorkBuffer);
- }
-
- ShowMessage("Removed incomplete file...\n");
- }
- } else {
- ReturnCode = IoErr();
-
- if(!((BOOL)ArgResult[OPT_QUIET])) {
- Fault(ReturnCode, NULL, WorkBuffer, 80);
- printf("Unable to open %s for writing: %s\n", TargetName, WorkBuffer);
- }
- }
-
- Close(SourceFile);
- // This one shouldn't really happen but anyway....
- } else {
- ReturnCode = IoErr();
-
- if(!((BOOL)ArgResult[OPT_QUIET])) {
- Fault(ReturnCode, NULL, WorkBuffer, 80);
- printf("Unable to open %s for reading: %s\n", TargetName, WorkBuffer);
- }
- }
-
- FreeVec(CopyBuffer);
- } else {
- ReturnCode = ERROR_NO_FREE_STORE;
- ShowMessage("Unable to allocate the copy buffer");
- }
-
- return(ReturnCode);
- }/*GFE*/
-
-
- /* void CopyAttributes(FileInfoblock *, STRPTR) */
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
- /* If the file has been copied then some attributes of the source may need */
- /* to be copied to the target: if the clone has been activated then all the */
- /* file attributes of the source need to be copied, otherwise the only attr. */
- /* copied by default is the protection bits - this cane be disabled with a */
- /* flag and other attributes can be activated with the DATE and COM args.. */
- /* */
- /* Parameters: */
- /* SourceInfo FileInfoBlock containing the source attributes */
- /* TargetName Name of the target file (must exist!!) */
-
- /*GFS*/ void CopyAttributes(struct FileInfoBlock *SourceInfo, STRPTR TargetName)
- {
- if((BOOL)ArgResult[OPT_CLONE]) {
- SetComment (TargetName, SourceInfo -> fib_Comment);
- SetFileDate (TargetName, &SourceInfo -> fib_Date);
- SetProtection(TargetName, SourceInfo -> fib_Protection);
- } else {
- if((BOOL)ArgResult[OPT_COM] ) SetComment (TargetName, SourceInfo -> fib_Comment);
- if((BOOL)ArgResult[OPT_DATE]) SetFileDate (TargetName, &SourceInfo -> fib_Date);
- if(!((BOOL)ArgResult[OPT_NOPRO])) SetProtection(TargetName, SourceInfo -> fib_Protection);
- }
- }/*GFE*/
-
-
- /* LONG VersionCopy(FileInfoBlock *, FileInfoBlock *, STRPTR, STRPTR, ULONG) */
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
- /* This checks the source and target versions and copies the source over the */
- /* target if the target version is lower than the source or the target has */
- /* no version information. */
- /* */
- /* Parameters: */
- /* SourceFIB FileInfoBlock containing the source attributes */
- /* TargetFIB FileInfoBlock containing the target attributes */
- /* SourceName Filename of the source file */
- /* TargetName Filename of the target */
- /* BufferSize Size of the copy buffer in bytes */
-
- /*GFS*/ LONG VersionCopy(struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
- {
- struct VersionData *SourceVers;
- struct VersionData *TargetVers;
- BPTR SourceFile;
- BPTR TargetFile;
- LONG ErrorCode = RETURN_WARN;
-
- // Open the source and get it's version - these must both succeed..
- if(SourceFile = Open(SourceName, MODE_OLDFILE)) {
- if(SourceVers = FindVersion(SourceFile, SourceFIB)) {
-
- // Open the target & get version info if possible...
- if(TargetFile = Open(TargetName, MODE_OLDFILE)) {
- if(TargetVers = FindVersion(TargetFile, TargetFIB)) {
-
- // Don't need these any more...
- Close(SourceFile);
- Close(TargetFile);
- SourceFile = 0L;
-
- // If the names match, or ignore has been set, then pass through..
- if((BOOL)ArgResult[OPT_IGNORE] || !strcmp(SourceVers -> Name, TargetVers -> Name)) {
-
- // Check the version and revision
- if((SourceVers -> Version > TargetVers -> Version) ||
- ((SourceVers -> Version == TargetVers -> Version) &&
- (SourceVers -> Revision > TargetVers -> Revision))) {
-
- // Source is newer - copy it.
- ErrorCode = FileCopy(SourceName, TargetName, BufferSize);
- } else {
-
- // Neil says he wants an OK for this case...
- ErrorCode = RETURN_OK;
- ShowMessage("Target is up to date");
- }
- } else {
- ErrorCode = ERROR_OBJECT_WRONG_TYPE;
- ShowMessage("Version names in source and target do not match");
- }
-
- FreeVersion(TargetVers);
-
- // No version info in the file.. just write over it.
- } else {
-
- // Don't need these now..
- Close(TargetFile);
- Close(SourceFile);
- SourceFile = 0L;
-
- // Target doesn't contain any version, straight copy
- ErrorCode = FileCopy((STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
-
- // If the copy was successful then copy the file attributes
- if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
- }
- } else {
-
- // Hmm.. opening the target failed. We know it exists (or we wouldn't be here)
- // but it won't open so something screwy is going on..
- ErrorCode = IoErr();
- if(!((BOOL)ArgResult[OPT_QUIET])) {
- Fault(ErrorCode, NULL, WorkBuffer, 80);
- printf("Unable to open target: %s\n", WorkBuffer);
- }
- }
-
- FreeVersion(SourceVers);
- } else {
- ErrorCode = IoErr();
- }
-
- if(SourceFile) Close(SourceFile);
- } else {
- ErrorCode = IoErr();
- if(!((BOOL)ArgResult[OPT_QUIET])) {
- Fault(ErrorCode, NULL, WorkBuffer, 80);
- printf("Unable to open source: %s\n", WorkBuffer);
- }
- }
-
- return(ErrorCode);
- }/*GFE*/
-
-
- int main(int argc, char **argv)
- {
- struct RDArgs *ArgsData ;
- struct FileInfoBlock *SourceFIB ;
- struct FileInfoBlock *TargetFIB ;
- BPTR SourceLock;
- BPTR TargetLock;
- STRPTR TargetName;
- STRPTR TempString;
- BOOL FreeTarget = FALSE;
- ULONG TargetSize = 0L;
- ULONG BufferSize = 0L;
-
- LONG ErrorCode = 0L;
-
- // Can't do anything if the user stuffs up the command line...
- if(ArgsData = ReadArgs(ARGS_TEMPLATE, ArgResult, NULL)) {
-
- // Work out how big the copy buffer should be
- if(ArgResult[OPT_BUFFER]) {
- BufferSize = (*(ULONG *)ArgResult[OPT_BUFFER]) * 512;
- }
-
- if(BufferSize == 0) BufferSize = 102400;
-
- // Allocate the source fib, lock the source and examine it
- if(SourceFIB = AllocDosObject(DOS_FIB, NULL)) {
- if(SourceLock = Lock((STRPTR)ArgResult[OPT_FROM], SHARED_LOCK)) {
- Examine(SourceLock, SourceFIB);
-
- // Only continue if the source is a file (no directory copy in this version...)
- if(SourceFIB -> fib_DirEntryType < 0) {
-
- // Quick hack to make sure we have a valid destination name..
- if(strlen((STRPTR)ArgResult[OPT_TO]) == 0) {
- TargetName = FilePart((STRPTR)ArgResult[OPT_FROM]);
- } else {
- // User has specified a destination (ie: not "") but is it a dir?
- TargetName = (STRPTR)ArgResult[OPT_TO];
-
- // If the target is a directory we need to add the filename...
- if(IsDirectory(TargetName)) {
- TempString = FilePart((STRPTR)ArgResult[OPT_FROM]);
- TargetSize = strlen(TargetName) + strlen(TempString) + 3;
-
- // Allocate space for the new name
- if(TargetName = AllocVec(TargetSize, MEMF_ANY)) {
- strcpy(TargetName, (STRPTR)ArgResult[OPT_TO]);
- AddPart(TargetName, TempString, TargetSize);
- FreeTarget = TRUE;
- } else {
- ErrorCode = ERROR_NO_FREE_STORE;
- ShowMessage("Unable to allocate target name buffer");
- }
- }
- }
-
- if(TargetName) {
-
- // Alloc fib, lock and examine as usual...
- if(TargetFIB = AllocDosObject(DOS_FIB, NULL)) {
- if(TargetLock = Lock(TargetName, SHARED_LOCK)) {
- Examine(TargetLock, TargetFIB);
-
- // Don't need these no more...
- UnLock(TargetLock);
- UnLock(SourceLock);
- SourceLock = 0L;
-
- // Copy only if the version wills it..
- ErrorCode = VersionCopy(SourceFIB, TargetFIB, (STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
-
- // If the copy was successful then copy the file attributes
- if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
-
- } else {
- ErrorCode = IoErr();
-
- // Can't lock the target - is it just that it doesn't exist....
- if(ErrorCode == ERROR_OBJECT_NOT_FOUND) {
-
- // Target doesn't exist, straight copy
- ErrorCode = FileCopy((STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
-
- // If the copy was successful then copy the file attributes
- if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
-
- } else {
-
- // .... or is it something worse?
- if(!((BOOL)ArgResult[OPT_QUIET])) {
- Fault(ErrorCode, NULL, WorkBuffer, 80);
- printf("Target not locked: (%ld) %s\n", ErrorCode, WorkBuffer);
- }
- }
- }
- FreeDosObject(DOS_FIB, TargetFIB);
- }
-
- if(FreeTarget) FreeVec(TargetName);
- }
- } else {
- ErrorCode = ERROR_OBJECT_WRONG_TYPE;
- ShowMessage("The source is not a file");
- }
-
- if(SourceLock) UnLock(SourceLock);
- } else {
- ErrorCode = IoErr();
- if(!((BOOL)ArgResult[OPT_QUIET])) {
- Fault(ErrorCode, NULL, WorkBuffer, 80);
- printf("Unable to open %s: %s\n", (STRPTR)ArgResult[OPT_FROM], WorkBuffer);
- }
- }
-
- FreeDosObject(DOS_FIB, SourceFIB);
- } else {
- ErrorCode = ERROR_NO_FREE_STORE;
- ShowMessage("Unable to allocate source FIB\n");
- }
-
- FreeArgs(ArgsData);
- } else {
- printf("%s: required argument missing\n", argv[0]);
- ErrorCode = ERROR_REQUIRED_ARG_MISSING;
- }
-
- exit(ErrorCode);
- }
-
-
-
-
-
-
-
-